/*
 *    This file is part of SSHTunneler.
 *
 *    Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
 *
 *    SSHTunneler is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    SSHTunneler is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with SSHTunneler.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package sshtunneler.library;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

public abstract class ServerTunnelFile<E extends Server> {

	/*** File Information ***/
	public static String extension = ".tun";
	public static ExtensionFilter filter = null;

	/*** XML Information ***/
	private static String ROOT_ELEMENT_NAME = "SSHTunnelerServers_Tunnels";
	private static String SERVER_ELEMENT_NAME = "Server";
	private static String SERVER_HOSTNAME_ATTR_NAME = "Hostname";
	private static String SERVER_PORT_ATTR_NAME = "Port";
	private static String SERVER_USERNAME_ATTR_NAME = "Username";
	private static String TUNNEL_ELEMENT_NAME = "Tunnel";
	private static String TUNNEL_LOCAL_PORT_ATTR_NAME = "LocalPort";
	private static String TUNNEL_REMOTE_SERVER_ATTR_NAME = "RemoteServer";
	private static String TUNNEL_REMOTE_PORT_ATTR_NAME = "RemotePort";

	{
		filter = new ExtensionFilter("SSH Tunneler Server/Tunnel file");
		filter.add_accepted_extension(extension);
	}

	public ServerTunnelFile() {
	}

	public abstract E createServer(String username, String hostname, int port);

	public final Document buildDocumentFromConfig(List<E> servers) {
		Element root = new Element(ROOT_ELEMENT_NAME);
		Document document = new Document(root);

		for (Server server : servers) {
			Element xml_server = new Element(SERVER_ELEMENT_NAME);
			root.addContent(xml_server);

			Attribute hostname = new Attribute(SERVER_HOSTNAME_ATTR_NAME,
					server.getHostname());
			Attribute port = new Attribute(SERVER_PORT_ATTR_NAME, Integer
					.toString(server.getPort()));
			Attribute username = new Attribute(SERVER_USERNAME_ATTR_NAME,
					server.getUsername());

			xml_server.setAttribute(hostname);
			xml_server.setAttribute(port);
			xml_server.setAttribute(username);

			for (Tunnel tunnel : server.getTunnels()) {
				Element xml_tunnel = new Element(TUNNEL_ELEMENT_NAME);
				xml_server.addContent(xml_tunnel);

				Attribute local_port = new Attribute(
						TUNNEL_LOCAL_PORT_ATTR_NAME, Integer.toString(tunnel
								.getLocalPort()));
				Attribute remote_server = new Attribute(
						TUNNEL_REMOTE_SERVER_ATTR_NAME, tunnel
								.getRemoteServer());
				Attribute remote_port = new Attribute(
						TUNNEL_REMOTE_PORT_ATTR_NAME, Integer.toString(tunnel
								.getRemotePort()));

				xml_tunnel.setAttribute(local_port);
				xml_tunnel.setAttribute(remote_server);
				xml_tunnel.setAttribute(remote_port);
			}
		}

		return document;
	}

	public final List<E> buildConfigFromDocument(Document document) {
		Element root = document.getRootElement();
		List<?> server_list = root.getChildren(SERVER_ELEMENT_NAME);
		List<E> servers = new ArrayList<E>();

		Iterator<?> i = server_list.iterator();
		while (i.hasNext()) {
			Element xml_server = (Element) i.next();
			String hostname = xml_server
					.getAttributeValue(SERVER_HOSTNAME_ATTR_NAME);
			String str_port = xml_server
					.getAttributeValue(SERVER_PORT_ATTR_NAME);
			String username = xml_server
					.getAttributeValue(SERVER_USERNAME_ATTR_NAME);

			int port;

			try {
				port = Integer.parseInt(str_port);
			} catch (NumberFormatException e) {
				port = 22;
			}
			E server = this.createServer(username, hostname, port);

			Iterator<?> i2 = xml_server.getChildren(TUNNEL_ELEMENT_NAME)
					.iterator();
			while (i2.hasNext()) {
				Element xml_tunnel = (Element) i2.next();
				String str_local_port = xml_tunnel
						.getAttributeValue(TUNNEL_LOCAL_PORT_ATTR_NAME);
				String remote_server = xml_tunnel
						.getAttributeValue(TUNNEL_REMOTE_SERVER_ATTR_NAME);
				String str_remote_port = xml_tunnel
						.getAttributeValue(TUNNEL_REMOTE_PORT_ATTR_NAME);

				int local_port;
				int remote_port;

				try {
					local_port = Integer.parseInt(str_local_port);
				} catch (NumberFormatException e) {
					local_port = 9999;
				}
				try {
					remote_port = Integer.parseInt(str_remote_port);
				} catch (NumberFormatException e) {
					remote_port = 22;
				}

				server.addTunnel(remote_server, remote_port, local_port);
			}
			servers.add(server);
		}
		return servers;
	}

	public final void save(File file, List<E> servers) throws IOException {
		Document document = this.buildDocumentFromConfig(servers);
		FileOutputStream output = new FileOutputStream(file);

		XMLOutputter writter = new XMLOutputter(Format.getPrettyFormat());
		writter.output(document, output);

		output.close();
	}

	public final List<E> load(File file) throws JDOMException, IOException {
		if (file == null) {
			return null;
		}

		SAXBuilder sxb = new SAXBuilder();
		Document document = sxb.build(file);

		List<E> servers = this.buildConfigFromDocument(document);

		return servers;
	}
}
